package Modele;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;


/**
 * Classe qui écrit une expérience avec curriculum learning
 */
public class WriterCurric {


        /**
         * Méthode permettant d'écrire une expérience avec curriculum learning, et une sans
         * @param fichier le fichier de l'expérience
         * @param nbApprentissage le nombre d'exemple donnes en apprentissage
         * @param nbTest le nombre d'exemple donnes en test
         * @param nbInputsInit le nombre d'entrees pour chaque exemple, premiere valeur testee         
         * @param nbInputsMax le nombre d'entrees pour chaque exemple, valeur maximum testee
         * @param pasInput le pas entre chaque valeur d'input testee
         * @param nbIterations nombre d'itérations
         * @param learningRate pas de la descente de gradient
         * @return
         * @throws IOException
         */
        public static boolean writeFichierCurric(File fichier, int nbApprentissage, int nbTest, int nbInputsInit, int nbInputsMax, int pasInput, int nbIterations, double learningRate) throws IOException
        {
              FileWriter f = new FileWriter(fichier,false);

			  f.write("require \"lab\"\n");
			  f.write("require \"random\"\n");
			  f.write("require \"nn\"\n");
			  f.write("require \"nn.Piad\"\n\n");
			
			  f.write("nbApp = "+nbApprentissage+"\n");
			  f.write("nbTest = "+nbTest+"\n");
			  f.write("nbInputs = "+nbInputsInit+"\n\n");
			  f.write("function sign(valeur) if valeur < 0 then return -1 else return 1 end end\n\n");
			
			  f.write("function genererX(nombre)\n");
			  f.write("local x = torch.Tensor(nombre,nbInputs)\n");
			  f.write("for i = 1,(nombre) do        \n");
			  f.write("x[i]:add(lab.rand(nbInputs))\n");
			  f.write("end\n");
			  f.write("return x\n");
			  f.write("end\n");
			
			  f.write("-- fonction qui genere des sorties aleatoirement\n");
			  f.write("function genererY(nombre,x,w)\n");
			  f.write("y = torch.Tensor(nombre)\n");
			  f.write("for i = 1,(nombre) do\n");
			  f.write("     y[i] = sign(x[i] * w[i])\n");
			  f.write("end\n");
			  f.write("return y\n");
			  f.write("end\n");
			
			
			  f.write("-- fonction qui insere dans la base d'apprentissage des donnees qui ne sont pas pertinentes\n");
			  f.write("function ajouteIrrel(x, nombre)\n");
			  f.write("for i=1,nombre do\n");
			  f.write("local nbIrrel = (lab.floor(lab.rand(1)*(nbInputs+1)))[1]\n");
			  f.write("if(nbIrrel > 0) then\n");
			  f.write("for j=1,nbIrrel do\n");
			  f.write("local indice = (lab.floor(lab.rand(1)*(nbInputs)))[1]+1\n");
			  f.write("if(lab.rand(1)[1] < 0.5) then\n");
			  f.write("x[i][indice] = 0\n");
			  f.write("else\n");
			  f.write("x[i][indice] = lab.rand(1)[1]\n");
			  f.write("end\n");
			  f.write("end  \n");
			  f.write("end\n");
			  f.write("end\n");
			  f.write("return x\n");
			  f.write("end\n");
			
			  f.write("-- fonction qui compte le nombre de 0. Ce nombre servira a quantifier la complexite de l'exemple.\n");
			  f.write("function compteZerosLigne(ligne)\n");
			  f.write("local cpt = 0\n");
			  f.write("for i=1,nbInputs do\n");
			  f.write("if ligne[i] == 0 then\n");
			  f.write("cpt = cpt + 1\n");
			  f.write("end\n");
			  f.write("end\n");
			  f.write("return cpt\n");
			  f.write("end\n");
			
			  f.write("-- fonction qui comptabilise le nombre total de 0.\n");
			  f.write("function compteZeros(nombre,x)\n");
			  f.write("local zeros = torch.Tensor(nombre)\n");
			  f.write("for i=1,nombre do\n");
			  f.write("zeros[i] = compteZerosLigne(x[i])\n");
			  f.write("end\n");
			  f.write("return zeros\n");
			  f.write("end\n");
			
			  f.write("-- fonction qui trie les exemples par difficulte croissante\n");
			  f.write("function sortByDiff(x,y,w,nombre,zeros)              \n");
			  f.write("zeros,indices = lab.sort(zeros)\n");
			  f.write("local xappTemp = torch.Tensor(nombre,nbInputs)\n");
			  f.write("local yappTemp = torch.Tensor(nombre)\n");
			  f.write("local wTemp = torch.Tensor(nombre,nbInputs)\n");
			  f.write("for i=1,nombre do\n");
			  f.write("yappTemp[i] = y[indices[i]]\n");
			  f.write("for j = 1,nbInputs do\n");
			  f.write("xappTemp[i][j] = x[indices[i]][j]\n");
			  f.write("wTemp[i][j] = w[indices[i]][j]\n");
			  f.write("end\n");
			  f.write("end\n");
			  f.write("x = xappTemp\n");
			  f.write("y = yappTemp\n");
			  f.write("w = wTemp\n");
			  f.write("return x,y,w\n");
			  f.write("end\n");
			
			  f.write("-- fonction qui genere les poids de la base.\n");
			  f.write("function genererPoids(nombre)\n");
			  f.write("return lab.randn(nombre,nbInputs)\n");
			  f.write("end\n");
			
			  f.write("-- tri des données pour la strategie de curriculum\n");
			  f.write("-- ie des données triées selon leur complexité\n");
			  f.write("function ajoutCurriculum(xapp,yapp,wapp,xtest,ytest,wtest)\n");
			  f.write("compteZeros(nbApp,xapp)\n");
			  f.write("compteZeros(nbTest,xtest)\n");
			  f.write("local zerosapp = compteZeros(nbApp,xapp)\n");
			  f.write("local zerostest = compteZeros(nbTest,xtest)\n");
			  f.write("xapp,yapp,wapp = sortByDiff(xapp,yapp,wapp,nbApp,zerosapp)\n");
			  f.write("xtest,ytest,wtest = sortByDiff(xtest,ytest,wtest,nbTest,zerostest)\n");
			  f.write("return xapp,yapp,wapp,xtest,ytest,wtest,zerosapp\n");
			  f.write("end\n");
			
			f.write("-- creation des donnees necessaires a une experience sans curriculum\n");
			f.write("function expSansCurriculum()\n");
			f.write("local xapp = genererX(nbApp)\n");
			f.write("local xtest = genererX(nbTest)\n");
			f.write("local wapp = genererPoids(nbApp)\n");
			f.write("local wtest = genererPoids(nbTest)\n");
			f.write("local yapp = genererY(nbApp, xapp, wapp)\n");
			f.write("local ytest = genererY(nbTest, xtest, wtest)\n");
			f.write("ajouteIrrel(xapp,nbApp)\n");
			f.write("ajouteIrrel(xtest,nbTest)\n");
			f.write("return xapp,yapp,wapp,xtest,ytest,wtest\n");
			f.write("end\n");
			
			f.write("function nbDiff(nombre, zeros)\n");
			f.write("cpt = 1\n");
			f.write("for i=2,nombre do\n");
			f.write("if zeros[i] > zeros[i-1] then\n");
			f.write("cpt = cpt + 1\n");
			f.write("end\n");
			f.write("end\n");
			f.write("return cpt\n");
			f.write("end\n");
			
			f.write("function lancerExpSansCurriculum()\n");
			f.write("file1 = \"erreurSans.txt\"\n");
			f.write("file1 = assert(io.open(file1,\"w\"))\n");

			f.write("while nbInputs <= "+nbInputsMax+" do\n");

			f.write("-- création de l'expérience\n");
			f.write("sanscurric = nn.Piad();\n");

			f.write("-- création du perceptron\n");
			f.write("mlpsc = nn.Sequential();\n");
			f.write("mlpsc:add(nn.Linear(nbInputs, 1))\n");
			f.write("mlpsc:add(nn.Tanh())\n");

			f.write("criterionsc = nn.MSECriterion()\n");
			f.write("trainersc = nn.StochasticGradient(mlpsc, criterionsc)\n");
			f.write("trainersc.learningRate = "+learningRate+"\n");
			f.write("trainersc.maxIteration = "+nbIterations+"\n");

			f.write("xapp1,yapp1,wapp1,xtest1,ytest1,wtest1 = expSansCurriculum()\n");

			f.write("print(\"Apprentissage\")\n");

			f.write("-- mise des données dans le format utilisé par torch\n");

			f.write("datasetasc={};\n");
			f.write("inputasc = torch.Tensor(nbInputs)\n");
			f.write("outputasc = torch.Tensor(1)\n");
			f.write("function datasetasc:size() return nbApp end\n");

			f.write("for i=1,nbApp do\n");
			f.write("for j =1,nbInputs do\n");
			f.write("inputasc[j] = xapp1[i][j]	\n");
			f.write("end\n");
			f.write("outputasc[1] = yapp1[i]\n");
			f.write("datasetasc[i] = {inputasc, outputasc}	\n");
			f.write("end\n");

			f.write("trainersc:train(datasetasc)\n");


			f.write("print(\"Test\")\n");

			f.write("file1:write(nbInputs)\n");
			f.write("file1:write(\" \")\n");

			f.write("-- mise des données dans le format utilisé par torch\n");

			f.write("datasettsc={};\n");
			f.write("inputtsc = torch.Tensor(nbInputs)\n");
			f.write("outputtsc = torch.Tensor(1)\n");
			f.write("function datasettsc:size() return nbTest end\n");

			f.write("for i=1,nbTest do\n");
			f.write("for j =1,nbInputs do\n");
			f.write("inputtsc[j] = xtest1[i][j]	\n");
			f.write("end\n");
			f.write("outputtsc[1] = ytest1[i]\n");
			f.write("datasettsc[i] = {inputtsc, outputtsc}	\n");
			f.write("end\n");

			f.write("u = trainersc:train3(datasettsc)\n");


			f.write("file1:write(u)\n");
			f.write("file1:write(\"\\n\")\n");
			f.write("nbInputs = nbInputs + "+pasInput+"\n");
			f.write("end\n");
			
			f.write("nbInputs = "+nbInputsInit+"\n");

			f.write("end\n");

			f.write("function lancerExpCurriculum()\n");
			f.write("file2 = \"erreurAvec.txt\"\n");
			f.write("file2 = assert(io.open(file2,\"w\"))\n");

			f.write("while nbInputs <= "+nbInputsMax+" do\n");

			f.write("-- création de l'expérience\n");
			f.write("curric = nn.Piad();\n");

			f.write("-- création du perceptron\n");
			f.write("mlpc = nn.Sequential();\n");
			f.write("mlpc:add(nn.Linear(nbInputs, 1))\n");
			f.write("mlpc:add(nn.Tanh())\n");

			f.write("criterionc = nn.MSECriterion()\n");
			f.write("trainerc = nn.StochasticGradient(mlpc, criterionc)\n");
			f.write("trainerc.learningRate = "+learningRate+"\n");
			f.write("trainerc.maxIteration = "+nbIterations+"\n");

			f.write("xapp2,yapp2,wapp2,xtest2,ytest2,wtest2,zerosapp = ajoutCurriculum(xapp1,yapp1,wapp1,xtest1,ytest1,wtest1)\n");

			f.write("indices = torch.Tensor(nbDiff(nbApp, zerosapp),2)\n");
			f.write("k=1\n");
			f.write("total = 0\n");
			f.write("indices[1][1] = 1\n");
			f.write("for u = 2,(nbApp) do\n");
			f.write("if zerosapp[u] > zerosapp[u-1] then\n");
			f.write("k = k + 1\n");
			f.write("indices[k][1] = u\n");
			f.write("indices[k-1][2] = u - indices[k-1][1]\n");
			f.write("		total = total + indices[k-1][2]\n");
			f.write("	end\n");
			f.write("end\n");
			f.write("indices[k][2] = nbApp - total\n");

			f.write(" print(\"Apprentissage\")\n");

			f.write("indiceCourant = 1\n");
			f.write("for t=1,nbDiff(nbApp, zerosapp) do\n");
			f.write(" datasetac={};\n");
			f.write(" local inputac = torch.Tensor(nbInputs)\n");
			f.write(" local outputac = torch.Tensor(1)\n");
			f.write(" function datasetac:size() return indices[t][2] end\n");
			f.write(" for k=1,indices[t][2] do\n");
			f.write("	 for j=1,nbInputs do\n");
			f.write("		 inputac[j] = xapp2[k][j]\n");
			f.write("	 end\n");
			f.write("	 outputac[1] = yapp2[indiceCourant]\n");
			f.write("	 datasetac[k] = {inputac, outputac}	\n");
			f.write(" end\n");
			f.write(" indiceCourant = indiceCourant + indices[t][2]\n");	
			f.write(" trainerc:train(datasetac)\n");
			f.write("end\n");

			f.write("print(\"Test\")\n");

			f.write("file2:write(nbInputs)\n");
			f.write("file2:write(\" \")\n");

			f.write("-- mise des données dans le format utilisé par torch\n");

			f.write("datasettc={};\n");
			f.write("inputtc = torch.Tensor(nbInputs)\n");
			f.write("outputtc = torch.Tensor(1)\n");
			f.write("function datasettc:size() return nbTest end\n");

			f.write("for i=1,nbTest do\n");
			f.write("	for j =1,nbInputs do\n");
			f.write("		inputtc[j] = xtest2[i][j]	\n");
			f.write("	end\n");
			f.write("	outputtc[1] = ytest2[i]\n");
			f.write("	datasettc[i] = {inputtc, outputtc}	\n");
			f.write("end\n");

			f.write("u = trainerc:train3(datasettc)\n");


			f.write("file2:write(u)\n");
			f.write("file2:write(\"\\n\")\n");
			f.write("nbInputs = nbInputs + "+pasInput+"\n");
			f.write("end\n");

			f.write("end\n");
	
			f.write("lancerExpSansCurriculum()\n");
			f.write("lancerExpCurriculum()\n");			

			f.close();

			return true;
		}
        
    

}
